#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <list>
#include <omp.h>
#include "mpi.h"
#define TRUE 1
#define FALSE 0
#define T_VETOR_PALAVRAS 1000
#define T_TAMANHO_PALAVRA 1001
#define T_PARADA 1002


using namespace std;

int main (int argc, char *argv[]) {

	int rc, qte_processos, rank, tam_nome;
	char nome_cluster[MPI_MAX_PROCESSOR_NAME];
	MPI_Status status;
    MPI_Request request;
    int end_loop = FALSE;
    int porcentagem;

	// inicializa o MPI
	rc = MPI_Init(&argc,&argv);

	if (rc != MPI_SUCCESS) {
		printf (">> main()::MPI_Init()\n");
		MPI_Abort(MPI_COMM_WORLD, rc);
	}

	omp_set_num_threads(2);

    // tratamento de erro de parametros
    if ((argc < 3) || (argc > 3)) {
                fprintf(stderr, ">> main()::argc\n");
                return (-1);
        }
    porcentagem= atoi(argv[2]);

	// pega a quantidade de processos que tem o programa
	MPI_Comm_size(MPI_COMM_WORLD,&qte_processos);
	// pega o numero do processo
	MPI_Comm_rank(MPI_COMM_WORLD,&rank);
	// pega o nome do processo
	MPI_Get_processor_name(nome_cluster, &tam_nome);

	printf("QteProc: %d - NroProc: %d - NomeProc: %s\n", qte_processos, rank, nome_cluster);





	if (rank == 0) {

		char chr;
		char str[100];
		bool eh_palavra;
		int tamanho_vetor_atual[2];
        int i,j;
        int terminou_leitura;
        char *v_palavras;
        char **vetor_palavras;
		list<string> lista_palavras_aleatorias;
		int qte_lida = 0;
		int task;


		FILE *arq;

		map<string,int> hash_table;
		int qte_palavras;



        vetor_palavras = (char**) malloc(sizeof(char*)*13);



		/*** Leitura do arquivo ***/

		if ((arq = fopen(argv[1], "r+")) == NULL) {
			fprintf(stderr, ">> main::fopen(%s)\n", argv[1]);
			return (-1);
		}

		i = 0;
		eh_palavra = true;
		while (!feof(arq)) {

			fread(&chr, sizeof(char), 1, arq);

			if ((chr >= 65) && (chr <= 90)) { // (65~90: letra maiuscula)
				// se eh letra maiuscula, converte pra minusculo e copia na string
				str[i] = chr + 32;
				i++;
			} else if ((chr >= 97) && (chr <= 122)) { // (97~122: letra minuscula)
				// se eh letra minuscula, copia direto na string
				str[i] = chr;
				i++;
			} else if ((chr == 13) || (chr == 44)) { // (13: \n) (44: ,)
				// fim de string
				str[i] = '\0';

				// soh adiciona na hash se nao foi ignorado a string
				if ((i != 0) && (eh_palavra == true)) {
					// adiciona na hash (chave = palavra, conteudo = tamanho da palavra)
					hash_table[str] = -1;
					// aumenta a quantidade de palavras
					qte_palavras++;
				}

				// zera para recomecar a leitura de uma nova palavra
				i = 0;
				// independente se for ou nao ignorada a string lida, comeca como se fosse uma palavra
				eh_palavra = true;
			} else { // qualquer outro caracter
				// ignora a string
				eh_palavra = false;
			}
		}
		fclose(arq);

		qte_palavras = (int) hash_table.size();

		end_loop = FALSE;
		while (!end_loop) {
		    // Recebe o tamanho do vetor
		    for (i=1; i<qte_processos;i++){
                MPI_Recv(&tamanho_vetor_atual,2,MPI_INT,i,T_TAMANHO_PALAVRA,MPI_COMM_WORLD, &status);

                v_palavras = (char *)malloc(sizeof(char)*tamanho_vetor_atual[0]+1);
                vetor_palavras[0] = (char*)malloc(sizeof(char)*tamanho_vetor_atual[0]+1);

                MPI_Recv(v_palavras,tamanho_vetor_atual[0],MPI_CHAR,i,T_VETOR_PALAVRAS,MPI_COMM_WORLD, &status);

                strcpy(vetor_palavras[tamanho_vetor_atual[1]],v_palavras);
		    }


			/*** transforma vetor em lista encadeada ***/

			terminou_leitura = FALSE;
			i = 0;
			j = 0;
			task = 0;
			while (!terminou_leitura){
				str[j] = vetor_palavras[tamanho_vetor_atual[1]][i];

				// se for fim de palavra
				if (vetor_palavras[tamanho_vetor_atual[1]][i] == ' ') {
					// fim de palavra
					str[j] = '\0';

					// adiciona no fim da lista
					lista_palavras_aleatorias.push_back(str);

					// reinicia str
					j = 0;
					i++;
				}

				// se for fim da string
				if (vetor_palavras[tamanho_vetor_atual[1]][i] == '\0') {
					// fim de palavra
					str[j] = '\0';

					// adiciona no fim da lista
					lista_palavras_aleatorias.push_back(str);

					// terminou leitura do vetor
					terminou_leitura = TRUE;
				}
                i++;
				j++;

			}


            /*** Comparacao se existe na hash ***/

            while(!lista_palavras_aleatorias.empty()){

                string s = lista_palavras_aleatorias.front();
                // se o tamanho for compativel, significa que a palavra foi achada, senao nao existe ou jah foi procurada
                if (hash_table.find(s)->second == -1) {//s.length()) {
                    // coloca como 0 se foi pesquisada
                    hash_table[s] = 0;
                    qte_lida++;

                    /** Compara e deve enviar o sinal de parada**/
                    if ( qte_lida >= (qte_palavras*porcentagem)/100) {
                        end_loop = TRUE;
                        // enviar end_loop para todos os nos
                        for(i=1;i<qte_processos;i++){
                            MPI_Isend(&end_loop,1,MPI_INT,i,T_PARADA,MPI_COMM_WORLD,&request);
                        }
                    }
                    else end_loop = FALSE;
                    //cout << ">> Rank 0: Quantidade Lida:  " << qte_lida << "\n";

                    // OBS: pode ateh deletar o elemento da hash, pois foi encontrado e nao precisa ser mais encontrado
                    //hash_table.erase(hash_table.find(s));
                }
                // deleta o primeiro elemento, pois jah foi pesquisado
                lista_palavras_aleatorias.pop_front();



            }


            // Envia end_loop para todos os nos
            for(i=1;i<qte_processos;i++){
                MPI_Isend(&end_loop,1,MPI_INT,1,T_PARADA,MPI_COMM_WORLD,&request);
            }


            // free(vetor_palavras[0]);
        } /** End Loop **/

        cout << ">> RANK 0: Tarefas concluidas \n";




	} else {
		int i, j, k, prob;
		char chr;
		int nro_letras, nro_substrings;
		string vetor_palavras;
		char *vetor_ch;
		size_t tamanho_vetor[2];
		int qte_palavras = 100;



		srand(time(NULL)*rank+1);

		while (!end_loop) {
			// gera 100 palavras para ser enviadas em conjunto
			for (i = 0; i < qte_palavras; i++) {
				// numero de substrings geradas para montar uma palavra

				//pal =  1,  1 < ... <  5 = 12%
				//pal =  2,  5 < ... < 10 = 63%
				//pal =  3, 10 < ... < 15 = 23%
				//pal =  4, 15 < ... < 20 = 1%
				//pal =  5, 20 < ... < 25 = 0%
				//pal =  6, 25 < ... < 30 = 0%
				//pal =  7, 30 < ... < 35 = 0%
				//pal =  8, 35 < ... < 40 = 0%
				//pal =  9, 40 < ... < 45 = 1%
				//pal = 10, 45 < ... < 50 = 0%
				//prob = (rand() % 100);

				//if (prob < 12) {
				//	nro_substrings = 1;
				//} else if (prob >= 12 && prob < 75) {
				//	nro_substrings = 2;
				//} else if (prob >= 75 && prob < 98) {
				//	nro_substrings = 3;
				//} else if (prob == 98) {
				//	nro_substrings = 4;
				//} else if (prob == 99) {
				//	nro_substrings = 9;
				//}
				nro_substrings = (rand() % 9);

				// gera substrings de no maximo 5 palavras de acordo com o numero de substrings
				for (k = 0; k < nro_substrings; k++) {
					// sorteia um numero de letras entre 1 a 5
					nro_letras = (rand() % 5) + 1;

					// cria a palavra (apenas letras minusculas)
					for (j = 0; j < nro_letras; j++) {
						chr = (rand() % 26) + 97; // 97..122 ou a..z
						vetor_palavras = vetor_palavras + chr;
					}
				}

				// acrescenta espaco para separar as palavras
				vetor_palavras = vetor_palavras + ' ';
			}

			// pega o tamanho do vetor
			tamanho_vetor[0] = vetor_palavras.size();

			// Alloca o char
			vetor_ch = (char*)malloc(sizeof(char)*tamanho_vetor[0]+1);

			// String to Char
            strcpy(vetor_ch ,vetor_palavras.c_str());
            vetor_palavras.clear();
			vetor_ch[tamanho_vetor[0]] = '\0';
			vetor_palavras[tamanho_vetor[0]] = '\0';

			tamanho_vetor[1] = rank;

			// Manda o tamanho do vetor
			MPI_Send(&tamanho_vetor,2,MPI_INT,0, T_TAMANHO_PALAVRA, MPI_COMM_WORLD);

			// Manda o vetor_palavras
			MPI_Send(vetor_ch,tamanho_vetor[0],MPI_CHAR,0,T_VETOR_PALAVRAS,MPI_COMM_WORLD);


			// reseta o vetor de char
            strcpy(vetor_ch ,vetor_palavras.c_str());
			////////////////////////AKI pega o end_loop de outro local
//			cout << "Recebendo endloop\n";
            MPI_Recv(&end_loop,1,MPI_INT,0,T_PARADA,MPI_COMM_WORLD,&status);
//            free(vetor_ch);
		}

	}

	MPI_Finalize();
	return 0;
}



//	//busca, retorna 1 ou 0, se retornar outro numero, significa que nao existe
//	cout << "a => " << hash_table.find("a")->second << endl;
//
//	//mudando o conteudo boleano
//	hash_table["a"] = true;
//	cout << "a => " << hash_table.find("a")->second << endl;
//
//	//imprime a hash
//	map<string,int>::iterator it;
//	map<string,int>::key_compare mycomp;
//	mycomp = hash_table.key_comp();
//	it=hash_table.begin();
//	do {
//		cout << (*it).first << "; ";
//	} while ( mycomp((*it++).first, hash_table.rbegin()->first) );
//
//	//apaga hash
//	hash_table.erase(hash_table.find("a"));
//	cout << "a => " << hash_table.find("a")->second << endl;
//
//	//quantidade de palavras da hash
//	cout << "qte de palavras => " << (int) hash_table.size() << endl;



// int nro_letras;
// int nro_palavras;
// char **palavras;


// srand(time(NULL));

// nro_palavras = 50;
// palavras = (char **) malloc(sizeof(char *)*nro_palavras);



// for (i = 0; i < nro_palavras; i++) {
	// // sorteia um numero de letras entre 1 a 5
	// nro_letras = (rand() % 5) + 1;

	// // aloca espaco  para essa palavra
	// palavras[i] = (char *) malloc(sizeof(char)*nro_letras+1);

	// // cria a palavra (apenas letras minusculas)
	// for (j = 0; j < nro_letras; j++) {
		// palavras[i][j] = (rand() % 26) + 97; // 97..122 ou a..z
	// }
	// palavras[i][j] = '\0';
// }

